﻿
using System;
using System.Collections.Generic;
using System.Xml;
using System.Reflection;
using System.Globalization;

namespace TSP
{

static class IEnumerableExtender
{

public static T First<T>(this IEnumerable<T> ie)
{
IEnumerator<T> e = ie.GetEnumerator();
e.MoveNext();
return e.Current;
}

}


public struct Vertex
{
public int     outDegree;
public int     inDegree;
public string  description;

public override string ToString()
{
return string.Format("<{0},{1},{2}>",inDegree,outDegree,description);
}

}  //  struct Vertex


public struct Edge
{
public int from;
public int to;
public double weight;

public Edge(int from, int to, double weight)
{
this.from=from;
this.to=to;
this.weight=weight;
}

public override string ToString()
{
return string.Format("<{0},{1},{2}>",from,to,weight);
}

}  //  struct Edge


public delegate void VisitVertex(int nr,int c);

public delegate void VisitEdge(Edge e,int c);


public interface IGraph
{
bool Directed { get; }
int VerticesCount { get; }
Vertex Vertices(int nr);
string SetVertexDescription(int nr, string desc);
IGraph Clone();
IGraph IsolatedVerticesGraph(); // "konstruktor wirtualny"
bool AddEdge(int from, int to, double weight);
bool AddEdge(int from, int to);
bool DelEdge(int from, int to);
void AddVertex();
void DelVertex(int nr);
IEnumerable<Edge> OutEdges(int from);
// konstruktor(IGraph)
// konstruktor(bool,int)
}


public abstract class Graph : IGraph
{

protected bool directed;
protected Vertex[] vertices;

public Graph(bool directed, int verticesCount)
{
this.directed=directed;
vertices = new Vertex[verticesCount];
}

public bool Directed
{
get {
    return directed;
    }
}

public int VerticesCount
{
get {
    return vertices.Length;
    }
}

public Vertex Vertices(int nr)
{
return vertices[nr];
}

public string SetVertexDescription(int nr, string desc)
{
string s=vertices[nr].description;
vertices[nr].description=desc;
return s;
}

public abstract IGraph Clone();

public abstract IGraph IsolatedVerticesGraph();

public virtual bool AddEdge(int from, int to, double weight)
{
throw new NotSupportedException("Edge addition is not supported.");
}

public bool AddEdge(int from, int to)
{
return AddEdge(from,to,1.0);
}

public virtual bool DelEdge(int from, int to)
{
throw new NotSupportedException("Edge deletion is not supported.");
}

public virtual void AddVertex()
{
throw new NotSupportedException("Vertex addition is not supported.");
}

public virtual void DelVertex(int nr)
{
throw new NotSupportedException("Vertex deletion is not supported.");
}

public abstract IEnumerable<Edge> OutEdges(int from);

public static void Write(IGraph g,string name)
{
string s;
CultureInfo culture = new CultureInfo("en-US");
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.IndentChars = "    ";

XmlWriter xml = XmlWriter.Create(name+".xml",settings);
xml.WriteStartDocument();
xml.WriteStartElement("Graph");
xml.WriteAttributeString("directed",g.Directed.ToString());
xml.WriteAttributeString("verticescount",g.VerticesCount.ToString());

xml.WriteStartElement("Vertices");
for ( int v=0 ; v<g.VerticesCount ; ++v )
    {
    s=g.Vertices(v).description;
    if ( s!=null )
        {
        xml.WriteStartElement("Vertex");
        xml.WriteAttributeString("ident",v.ToString());
        xml.WriteAttributeString("desc",s);
        xml.WriteEndElement();
        }
    }
xml.WriteEndElement();  // Vertices

xml.WriteStartElement("Edges");
for ( int v=0 ; v<g.VerticesCount ; ++v )
    foreach ( Edge e in g.OutEdges(v) )
        {
        xml.WriteStartElement("Edge");
        xml.WriteAttributeString("from",e.from.ToString());
        xml.WriteAttributeString("to",e.to.ToString());
        xml.WriteAttributeString("weight",string.Format(culture,"{0:R}",e.weight));
        xml.WriteEndElement();
        }
xml.WriteEndElement();  // Edges

xml.WriteEndElement();  // Graph
xml.WriteEndDocument();
xml.Close();
}

public static IGraph Read(Type t,string name)
{
CultureInfo culture = new CultureInfo("en-US");
bool dir=false;
int from, to, i;
double w;
string s;
IGraph g=null;
ConstructorInfo ci;

if ( t.GetInterface("IGraph")==null )
    throw new ArgumentException("Passed type doesn't implement interface IGraph");
XmlReader xml = XmlReader.Create(name+".xml");
while ( xml.Read() )
    {
    if ( xml.NodeType==XmlNodeType.Element )
        {
        switch ( xml.Name )
            {
            case "Graph":
                dir=bool.Parse(xml.GetAttribute("directed"));
                i=int.Parse(xml.GetAttribute("verticescount"));      
                ci = t.GetConstructor(new Type[]{typeof(bool),typeof(int)});
                g = ci.Invoke(new object[]{dir,i}) as IGraph;
                break;
            case "Vertex":
                i=int.Parse(xml.GetAttribute("ident"));
                s=xml.GetAttribute("desc");
                g.SetVertexDescription(i,s);
                break;
            case "Edge":
                from=int.Parse(xml.GetAttribute("from"));      
                to=int.Parse(xml.GetAttribute("to"));      
                w=double.Parse(xml.GetAttribute("weight"),culture);      
                g.AddEdge(from,to,w);
                break;
            }
        }
    }
xml.Close();
return (IGraph)g;
}

protected void Construct(IGraph g)
{
// zawsze graf ktory konstruujemy ma tyle samo wierzcholkow co graf g
for ( int v=0 ; v<g.VerticesCount ; ++v )
    {
    vertices[v].description = g.Vertices(v).description;
    foreach ( Edge e in g.OutEdges(v) )
        this.AddEdge(e.from,e.to,e.weight);
    }
}

}  //  class Graph

}  //  namespace ASD.Graphs
